package com.softa.common.sysconfig.config;

import com.softa.common.sysconfig.cache.ShiroCacheManager;
import com.softa.common.sysconfig.properties.PandaCoreProperties;
import com.softa.common.sysconfig.shiro.filter.KickoutSessionControlFilter;
import com.softa.common.sysconfig.shiro.filter.MyAuthenticatingFilter;
import com.softa.common.sysconfig.shiro.filter.SysUserFilter;
import com.softa.common.sysconfig.shiro.realm.AdminRealm;
import com.softa.common.sysconfig.shiro.session.dao.RedisSessionDAO;
import com.softa.common.sysconfig.support.ShiroFilterBean;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.SessionFactory;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.SimpleSessionFactory;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by yangl on 2018/5/20.
 */
@Configuration
@Import({CacheConfig.class})
@EnableConfigurationProperties({PandaCoreProperties.class})
public class ShiroConfig {

    /**
     * 凭证匹配器
     * @return
     */
//    @Bean
//    public RetryLimitHashedCredentialsMatcher credentialsMatcher(){
//        RetryLimitHashedCredentialsMatcher credentialsMatcher = new RetryLimitHashedCredentialsMatcher();
//        /**
//         *  <property name="hashAlgorithmName" value="md5"/>
//         <property name="hashIterations" value="1"/>
//         <property name="storedCredentialsHexEncoded" value="true"/>
//         */
//        credentialsMatcher.setHashAlgorithmName("md5");
//        //加密次数
//        credentialsMatcher.setHashIterations(1);
//        credentialsMatcher.setStoredCredentialsHexEncoded(Boolean.TRUE);
//        return credentialsMatcher;
//    }

    /**
     * Realm实现
//     * @param credentialsMatcher
     * @return
     */
    @Bean
    public AdminRealm adminRealm(){
        AdminRealm realm = new AdminRealm();
        /**
         * property name="credentialsMatcher" ref="credentialsMatcher"/>
         <property name="cachingEnabled" value="false"/>
         */
//        realm.setCredentialsMatcher(credentialsMatcher);
        realm.setCachingEnabled(Boolean.FALSE);
        return realm;
    }

    /**
     * 会话ID生成器
     * @return
     */
    @Bean
    public JavaUuidSessionIdGenerator sessionIdGenerator(){
        JavaUuidSessionIdGenerator javaUuidSessionIdGenerator = new JavaUuidSessionIdGenerator();
        return javaUuidSessionIdGenerator;
    }

    /**
     * 会话Cookie模板
     * @return
     */
    @Bean
    public SimpleCookie sessionIdCookie(){
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        /**
         * <constructor-arg value="sid"/>
         <property name="httpOnly" value="true"/>
         <property name="maxAge" value="-1"/>
         */
        simpleCookie.setHttpOnly(Boolean.TRUE);
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    @Bean
    public SimpleCookie rememberMeCookie(){
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        /**
         * <constructor-arg value="rememberMe"/>
         <property name="httpOnly" value="true"/>
         <property name="maxAge" value="2592000"/>
         */
        simpleCookie.setHttpOnly(Boolean.TRUE);
        //30天
        simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }

    /**
     * rememberMe管理器
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        /**
         <property name="cipherKey"
         value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
         <property name="cookie" ref="rememberMeCookie"/>
         */
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度（128 256 512 位）
        byte[] bytes = Base64.decode("4AvVhmFLUs0KTA3Kprsdag==");
        cookieRememberMeManager.setCipherKey(bytes);
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    /**
     * 会话DAO
     * @return
     */
    @Bean
    public SessionDAO sessionDAO(){
        RedisSessionDAO enterpriseCacheSessionDAO = new RedisSessionDAO();
        /**
         * <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
         <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
         */
//        enterpriseCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        enterpriseCacheSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        return enterpriseCacheSessionDAO;
    }

    /**
     * 会话管理器
     * @return
     */
    @Bean
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        /**
         * <property name="globalSessionTimeout" value="1800000"/>
         <property name="deleteInvalidSessions" value="true"/>
         <property name="sessionValidationSchedulerEnabled" value="true"/>
         <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
         <property name="sessionDAO" ref="sessionDAO"/>
         <property name="sessionIdCookieEnabled" value="true"/>
         <property name="sessionIdCookie" ref="sessionIdCookie"/>
         */
        sessionManager.setGlobalSessionTimeout(1800000);
//        sessionManager.setDeleteInvalidSessions(Boolean.TRUE);
//        sessionManager.setSessionValidationSchedulerEnabled(Boolean.TRUE);
//        sessionManager.setSessionValidationScheduler(sessionValidationScheduler(cacheManager));
        sessionManager.setSessionDAO(sessionDAO());
//        sessionManager.setSessionIdCookieEnabled(Boolean.TRUE);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setSessionFactory(sessionFactory());
        return sessionManager;
    }

    @Bean
    protected SessionFactory sessionFactory() {
        return new SimpleSessionFactory();
    }

//    @Bean
//    @ConditionalOnBean(value = {DefaultWebSessionManager.class})
//    public MySessionValidationScheduler sessionValidationScheduler(SpringCacheManagerWrapper cacheManager){
//        MySessionValidationScheduler sessionValidationScheduler = new MySessionValidationScheduler();
//        /**
//         * <property name="interval" value="60000"/>
//         <property name="sessionManager" ref="sessionManager"/>
//         */
//        sessionValidationScheduler.setInterval(60000);
//        sessionValidationScheduler.setSessionManager(sessionManager(cacheManager));
//        return  sessionValidationScheduler;
//    }

    /**
     * 安全管理器
     * @param adminRealm
     * @param cacheManager
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(AdminRealm adminRealm, ShiroCacheManager cacheManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        /**
         * <!-- filter&realm绑定 -->
         <!--<property name="authenticator" ref="defineModularRealmAuthenticator" />-->
         <property name="realm" ref="adminRealm"/>
         <property name="sessionManager" ref="sessionManager"/>
         <property name="cacheManager" ref="cacheManager"/>
         <property name="rememberMeManager" ref="rememberMeManager"/>
         */
        securityManager.setRealm(adminRealm);
        securityManager.setSessionManager(sessionManager());
        securityManager.setCacheManager(cacheManager);
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 相当于调用SecurityUtils.setSecurityManager(securityManager)
     * @param securityManager
     * @return
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager){
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        /**
         * <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
         <property name="arguments" ref="securityManager"/>
         */
        methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        methodInvokingFactoryBean.setArguments(securityManager);
        return methodInvokingFactoryBean;
    }

//    @Bean
//    public AuthorizationAttributeSourceAdvisor authorizationAdvisor(
//            org.apache.shiro.mgt.SecurityManager securityManager) {
//        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
//        advisor.setSecurityManager(securityManager);
//        return advisor;
//    }

    @Bean
    public SysUserFilter sysUserFilter(){
        SysUserFilter sysUserFilter = new SysUserFilter();
        return sysUserFilter;
    }

    @Bean
    public MyAuthenticatingFilter jwtFilter(PandaCoreProperties pandaCoreProperties){
        return new MyAuthenticatingFilter(pandaCoreProperties);
    }

    /**
     * 用于控制并发登录人数
     * @return
     */
    @Bean
    public KickoutSessionControlFilter kickoutSessionControlFilter(){
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        /**
         * <property name="cacheManager" ref="cacheManager"/>
         <property name="sessionManager" ref="sessionManager"/>
         <property name="kickoutAfter" value="false"/>
         <property name="maxSession" value="1"/>
         <property name="kickoutUrl" value="/custom/login?kickout=1"/>
         */
        kickoutSessionControlFilter.setCacheManager();
        kickoutSessionControlFilter.setSessionManager(sessionManager());
        kickoutSessionControlFilter.setKickoutAfter(Boolean.FALSE);
        //同一个用户最大的会话数，默认 1；比如 2 的意思是同一个用户允许最多同时两个人登录
        kickoutSessionControlFilter.setMaxSession(1);
        kickoutSessionControlFilter.setKickoutUrl("/login?kickout=1");
        return kickoutSessionControlFilter;
    }

    /**
     * Shiro生命周期处理器
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor();
        return lifecycleBeanPostProcessor;
    }

//    @Bean
//    @DependsOn("lifecycleBeanPostProcessor")
//    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
//        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
//        creator.setProxyTargetClass(true);
//        return creator;
//    }

    /**
     * Shiro的Web过滤器
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager, PandaCoreProperties pandaCoreProperties){
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/login");
        shiroFilter.setSuccessUrl("/");
//        shiroFilter.setUnauthorizedUrl("/");
        Map<String, Filter> filters = new LinkedHashMap<>();
//        filters.put("form", formAuthenticationFilter());
        filters.put("sysUser", sysUserFilter());
        filters.put("kickout", kickoutSessionControlFilter());
        filters.put("jwt", jwtFilter(pandaCoreProperties));
        shiroFilter.setFilters(filters);
        //filter
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/logout", "logout");
        //设置其他filter
        List<ShiroFilterBean> otherFilters = pandaCoreProperties.getShiroOtherFilter();
        if(CollectionUtils.isNotEmpty(otherFilters)){
            for(ShiroFilterBean bean : otherFilters){
                filterChainDefinitionMap.put(bean.getUrl(), bean.getFilter());
            }
        }
        //设置不拦截的url
        List<String> anonFilters = pandaCoreProperties.getShiroAnonFilter();
        if(CollectionUtils.isNotEmpty(anonFilters)){
            for(String value : anonFilters){
                filterChainDefinitionMap.put(value, "anon");
            }
        }
        filterChainDefinitionMap.put("/**", "kickout,sysUser,jwt");
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilter;
    }

}
